﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Diagnostics.Contracts;
using System.Windows.Data;
using ShredTheLog.Common.MVVM;

namespace ShredTheLog.Controls
{
    public static class ViewFactory
    {
        public static void Show(this Window view, ViewModelBase viewModel)
        {
            Contract.Requires(view != null);
            Contract.Requires(viewModel != null);

            view.DataContext = viewModel;
            view.Show();
        }

        public static void RecreateViews(Action action)
        {
            Contract.Requires(action != null);

            // Запоминаем информацию об открытых окнах
            var windows = Application.Current.Windows;
            Contract.Assume(windows != null);
            var typedWindowsList = windows.OfType<Window>().ToArray();
            var viewInfo = (from w in typedWindowsList select new { ViewType = w.GetType(), ViewModel = (ViewModelBase)w.DataContext }).ToArray();
            // Шаг 1: закрываем окна
            var stage1errors = typedWindowsList.Select(
                w => 
                {
                    try
                    {
                        BindingOperations.ClearAllBindings(w);
                        w.Content = null;
                        w.DataContext = null;
                        w.Template = null;
                        w.Resources.Clear();
                        w.Close();
                    }
                    catch (Exception ex)
                    {
                        return ex;
                    }
                    return null;
                }).Where(ex => ex != null).ToArray();

            // Шаг 2: выполняем заданное действие
            Exception stage2error;
            try
            {
                action();
                stage2error = null;
            }
            catch (Exception ex)
            {
                stage2error = ex;
            }

            // Шаг 3: пересоздаем окна
            var stage3errors = viewInfo.Select(
                vi =>
                {
                    try
                    {
                        var view = (Window)Activator.CreateInstance(vi.ViewType);
                        view.Show(vi.ViewModel);
                    }
                    catch (Exception ex)
                    {
                        return ex;
                    }
                    return null;
                }).Where(ex => ex != null).ToArray();

            var errors = 
                stage1errors.
                Concat(new Exception[] { stage2error }.Where(ex => ex != null)).
                Concat(stage3errors).ToArray();

            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            GC.WaitForPendingFinalizers();
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);

            if (errors.Length != 0)
                throw new AggregateException(errors);
        }
    }
}
